package com.fiberhome.ztelauncher.resource;

import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;

import com.fiberhome.app.network.CacheBuilder;
import com.fiberhome.app.network.CallBack;
import com.fiberhome.app.network.Failure;
import com.fiberhome.app.network.NetUtil;
import com.fiberhome.app.network.download.DownloadBean;
import com.fiberhome.app.network.download.DownloadListener;
import com.fiberhome.app.network.download.DownloadManager;
import com.fiberhome.ztelauncher.util.MD5Util;
import com.fiberhome.ztelauncher.xml.XmlTransfer;
import com.fiberhome.ztelauncher.xml.bean.FileBean;
import com.fiberhome.ztelauncher.xml.bean.LaunchDataCommon;
import com.fiberhome.ztelauncher.xml.bean.LaunchDataPage;
import com.fiberhome.ztelauncher.xml.bean.Launcher;
import com.fiberhome.ztelauncher.xml.bean.LauncherLayout;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Locale;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;

/**
 * Created by Deed on 2018/3/20.
 */

public class ResourceManager {
    public static final int RESOURCE_ASSETS = 1;
    public static final int RESOURCE_URL = 2;
    
    private static final String GET_LAUNCHER_LOCATION_URL = "http://10.6.34.48:8080/HTML/useetv.html";

    private String locationXmlUrl = "http://10.6.34.48:8080/LauncherXML/UseeTV/TV/";

    private int resourceType = RESOURCE_URL;
    private Context appContext;

    private String launcherFile = "";
    private String resourcePath = "file:///android_asset/";

    private String resDir;
    private boolean needUpdateResource;

    private static ResourceManager resourceManager;
    private Launcher launcher;

    /**
     * return value.
     */
    public static ResourceManager getResourceManager() {
        synchronized (ResourceManager.class) {
            if (resourceManager == null) {
                resourceManager = new ResourceManager();
            }
        }
        return resourceManager;
    }

    /**
     *
     * @param appContext
     */
    public void init(Context appContext) {
        this.appContext = appContext;
        NetUtil.init(appContext);
//        DownloadManager.getInstance().init(appContext);
        resDir = appContext.getFilesDir() + "/" + "LauncherXml";
        File file = new File(resDir);
        if (!file.exists()) {
            if (!file.mkdir()) {
                throw new RuntimeException("Resource init failed");
            }
        }
        launcherFile = getLauncherFileName();
    }
    
    public Launcher currentLauncher() {
		return launcher;
	}

    /**
     * @param ob Observer
     */
    public void getLauncher(Observer<Launcher> ob) {
        try {
            InputStream is = getResourceIs(launcherFile);
            getLauncher(is, ob);
        } catch (IOException e) {
            e.printStackTrace();
            ob.onError(e);
        }
    }

    /**
     * @param ob Observer
     */
    public void getLauncher(final InputStream is, Observer<Launcher> ob) {
        Observable<Launcher> obs = Observable.create(new ObservableOnSubscribe<Launcher>() {
            @Override
            public void subscribe(@NonNull ObservableEmitter<Launcher> observableEmitter) throws Exception {
                Launcher l = XmlTransfer.parseTo(is, Launcher.class);
                if (l != null) {
                	launcher = l;
                    observableEmitter.onNext(l);
                } else {
                    observableEmitter.onError(new Exception("parse error"));
                }
                observableEmitter.onComplete();
                if (is != null) {
                    is.close();
                }
            }
        }).observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.newThread());
        obs.subscribe(ob);
    }

    /**
     *
     * @param ob
     */
    public void checkLauncher(final Observer<Launcher> ob) {
        String launcherPath = getResourceUrl(getLauncherFileName());
        File file = new File(launcherPath);
        if (file.exists()) {
            try {
                FileInputStream fileInputStream = new FileInputStream(launcherPath);
                getLauncher(fileInputStream, new Observer<Launcher>() {
                    @Override
                    public void onSubscribe(@NonNull Disposable disposable) {

                    }

                    @Override
                    public void onNext(@NonNull Launcher launcher) {
                        compareLauncher(launcher,ob);
                    }

                    @Override
                    public void onError(@NonNull Throwable throwable) {
                        ob.onError(throwable);
                        Log.e("ZTELauncher", throwable.getMessage());
                    }

                    @Override
                    public void onComplete() {

                    }
                });
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                ob.onError(e);
            }
        } else {
            compareLauncher(null,ob);
        }
    }

    /**
     *
     * @param oldLauncher
     * @param ob
     */
    public void compareLauncher(final Launcher oldLauncher, final Observer<Launcher> ob) {
    	Log.i("ZTELauncher", "start get launcherXml url =" + getRequestUrl(getLauncherFileName()));
        DownloadManager.getInstance().download(getRequestUrl(getLauncherFileName()), resDir, new DownloadListener() {
            @Override
            public void onUpdate(DownloadBean downloadBean) {
            }

            @Override
            public void onError(Throwable e) {
            	Log.e("ZTELauncher", e.getMessage());
            	ob.onError(e);
            }

            @Override
            public void onComplete(DownloadBean downloadBean) {
                try {
                    FileInputStream fileInputStream = new FileInputStream(downloadBean.filePath);
                    getLauncher(fileInputStream, new Observer<Launcher>() {
                        @Override
                        public void onSubscribe(@NonNull Disposable disposable) {
                            ob.onSubscribe(disposable);
                        }

                        @Override
                        public void onNext(@NonNull Launcher launcher) {
                            if (oldLauncher != null) {
                                if (!launcher.resource.version.equals(oldLauncher.resource.version)) {
                                    needUpdateResource = true;
                                } else {
                                    needUpdateResource = false;
                                }
                            } else {
                                needUpdateResource = true;
                            }
                            ob.onNext(launcher);
                        }

                        @Override
                        public void onError(@NonNull Throwable throwable) {
                            ob.onError(throwable);
                            Log.e("ZTELauncher", throwable.getMessage());
                        }

                        @Override
                        public void onComplete() {
                            ob.onComplete();
                        }
                    });
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                    ob.onError(e);
                    Log.e("ZTELauncher", e.getMessage());
                }
            }
        });
    }

    /**
     *
     * @param launcher
     * @param resourceCallBack
     */
    public void initResource(final Launcher launcher, final ResourceCallBack resourceCallBack) {

        final String path = getResourceUrl(launcher.resource.file);
//        String outPut = resDir + "/" + "des.zip";
//        try {
//            copy(outPut,launcher.resource.file);
//            unZipFile(outPut,resDir);
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
        final SharedPreferences sp = appContext.getSharedPreferences("launcherSetting", Activity.MODE_PRIVATE);
        String unZipVersion = sp.getString("unZipResource",null);
        if (!launcher.resource.version.equals(unZipVersion)) {
            File file = new File(path);
            if (!file.exists() || needUpdateResource) {
                updateResource(launcher,sp,resourceCallBack);
            } else {
                String md5 = MD5Util.fileToMD5(file);
                Log.i("ZTELauncher", "downloaded md5 =" + md5 + " : md5 =" + launcher.resource.filesign);
                if (md5.equals(launcher.resource.filesign)) {
                    unZipTask(path, resDir, new Observer<Boolean>() {
                        @Override
                        public void onSubscribe(@NonNull Disposable disposable) {

                        }

                        @Override
                        public void onNext(@NonNull Boolean aBoolean) {
                        	Log.i("ZTELauncher", "unZipFile " + (aBoolean ? "ok" : "fail"));
                            if (aBoolean) {
                                sp.edit().putString("unZipResource",launcher.resource.version).apply();
                                if (resourceCallBack != null) {
                                    resourceCallBack.onResourceReady();
                                }
                            } else {
                                if (resourceCallBack != null) {
                                    resourceCallBack.onResourceFailed();
                                }
                            }
                        }

                        @Override
                        public void onError(@NonNull Throwable throwable) {
                            if (resourceCallBack != null) {
                                resourceCallBack.onResourceFailed();
                            }
                            Log.e("ZTELauncher","unZipFile :" + throwable.getMessage());
                        }

                        @Override
                        public void onComplete() {
                        }
                    });
                } else {
                    updateResource(launcher,sp,resourceCallBack);
                }
            }
        } else {
            if (resourceCallBack != null) {
                resourceCallBack.onResourceReady();
            }
        }
    }

    /**
     *
     * @param launcher
     * @param sp
     * @param resourceCallBack
     */
    public void updateResource(final Launcher launcher, final SharedPreferences sp,
                               final ResourceCallBack resourceCallBack) {
        DownloadManager.getInstance().download(getRequestUrl(launcher.resource.file), resDir, new DownloadListener() {
            @Override
            public void onUpdate(DownloadBean downloadBean) {

            }

            @Override
            public void onError(Throwable e) {
            	Log.e("ZTELauncher", e.getMessage());
                if (resourceCallBack != null) {
                    resourceCallBack.onResourceFailed();
                }
            }

            @Override
            public void onComplete(DownloadBean downloadBean) {
                String md5 = MD5Util.fileToMD5(downloadBean.filePath);
                Log.i("ZTELauncher", "downloaded into " + downloadBean.filePath);
                Log.i("ZTELauncher", "downloaded md5 =" + md5 + " : md5 =" + launcher.resource.filesign);
                if (md5.equals(launcher.resource.filesign)) {
                    unZipTask(downloadBean.filePath.getAbsolutePath(), resDir, new Observer<Boolean>() {
                        @Override
                        public void onSubscribe(@NonNull Disposable disposable) {

                        }

                        @Override
                        public void onNext(@NonNull Boolean aBoolean) {
                            if (aBoolean) {
                                sp.edit().putString("unZipResource",launcher.resource.version).apply();
                                if (resourceCallBack != null) {
                                    resourceCallBack.onResourceReady();
                                }
                            } else {
                                if (resourceCallBack != null) {
                                    resourceCallBack.onResourceFailed();
                                }
                            }
                        }

                        @Override
                        public void onError(@NonNull Throwable throwable) {
                        	Log.e("ZTELauncher", throwable.getMessage());
                        	if (resourceCallBack != null) {
                                resourceCallBack.onResourceFailed();
                            }
                        }

                        @Override
                        public void onComplete() {

                        }
                    });
                } else {
                	Log.i("ZTELauncher", "downloaded fail ");
                    //download fail
                    if (resourceCallBack != null) {
                        resourceCallBack.onResourceFailed();
                    }
                }
            }
        });
    }

    public void release(Launcher launcher) {
        DownloadManager.getInstance().cancel(getRequestUrl(launcher.resource.file));
    }

    /**
     *
     * @param zipFile
     * @param outPut
     * @param ob
     */
    public void unZipTask(final String zipFile,final String outPut,Observer<Boolean> ob) {
    	Log.i("ZTELauncher", "start unzip " + zipFile);
        Observable<Boolean> observable = Observable.create(new ObservableOnSubscribe<Boolean>() {
            @Override
            public void subscribe(@NonNull ObservableEmitter<Boolean> observableEmitter) throws Exception {
                boolean isOk = unZipFile(zipFile,outPut);
                observableEmitter.onNext(isOk);
                observableEmitter.onComplete();
                Log.i("ZTELauncher", "unzip " + (isOk ? "ok" : "fail"));
            }
        }).observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.newThread());
        observable.subscribe(ob);
    }

    private String getLauncherFileName() {
        Locale locale = appContext.getResources().getConfiguration().locale;
        DisplayMetrics mDisplayMetrics = appContext.getResources().getDisplayMetrics();
        int h = mDisplayMetrics.heightPixels;
        if (locale.equals(Locale.ENGLISH) || locale.equals(Locale.US)) {
            return "launcher_" + h + "p_en.xml";
        } 
//        else if (locale.equals(Locale.CHINESE) || locale.equals(Locale.SIMPLIFIED_CHINESE)) {
//            return "launcher_" + h + "p.xml";
//        }
        return "launcher_" + h + "p_id.xml";
    }

    private String getRequestUrl(String relatePath) {
    	if (relatePath.startsWith("http")) {
			return relatePath;
		}
    	if (locationXmlUrl.endsWith("/")) {
			return locationXmlUrl + relatePath;
		}
        return locationXmlUrl + "/" + relatePath;
    }

    /**
     * @param layoutFile FileBean
     * @param ob         Observer
     */
    public void getLauncherLayout(final FileBean layoutFile, Observer<LauncherLayout> ob) {
        Observable<LauncherLayout> obs = Observable.create(new ObservableOnSubscribe<LauncherLayout>() {
            @Override
            public void subscribe(@NonNull ObservableEmitter<LauncherLayout> observableEmitter) throws Exception {
                InputStream is = getResourceIs(layoutFile.file);
                LauncherLayout launcherLayout = XmlTransfer.parseTo(is, LauncherLayout.class);
                if (launcherLayout != null) {
                    observableEmitter.onNext(launcherLayout);
                } else {
                    observableEmitter.onError(new Exception("parse error"));
                }
                observableEmitter.onComplete();
                if (is != null) {
                    is.close();
                }
            }
        }).observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.newThread());
        obs.subscribe(ob);
    }

    public void getCommonData(final FileBean commonFile, Observer<LaunchDataCommon> ob) {
        Observable<LaunchDataCommon> obs = Observable.create(new ObservableOnSubscribe<LaunchDataCommon>() {
            @Override
            public void subscribe(@NonNull ObservableEmitter<LaunchDataCommon> observableEmitter) throws Exception {
                InputStream is = getResourceIs(commonFile.file);
                LaunchDataCommon l = XmlTransfer.parseTo(is, LaunchDataCommon.class);
                if (l != null) {
                    observableEmitter.onNext(l);
                } else {
                    observableEmitter.onError(new Exception("parse error"));
                }
                observableEmitter.onComplete();
            }
        }).observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.newThread());
        obs.subscribe(ob);
    }

    public void getPageData(final FileBean pageFile, Observer<LaunchDataPage> ob) {
        Observable<LaunchDataPage> obs = Observable.create(new ObservableOnSubscribe<LaunchDataPage>() {
            @Override
            public void subscribe(@NonNull ObservableEmitter<LaunchDataPage> observableEmitter) throws Exception {
                InputStream is = getResourceIs(pageFile.file);
                LaunchDataPage l = XmlTransfer.parseTo(is, LaunchDataPage.class);
                if (l != null) {
                    observableEmitter.onNext(l);
                } else {
                    observableEmitter.onError(new Exception("parse error"));
                }
                observableEmitter.onComplete();
                if (is != null) {
                    is.close();
                }
            }
        }).observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.newThread());
        obs.subscribe(ob);
    }

    private InputStream getResourceIs(String path) throws IOException {
        if (resourceType == RESOURCE_ASSETS) {
            return appContext.getAssets().open(path);
        }
        return new FileInputStream(getResourceUrl(path));
    }

    public String getResourceUrl(String file) {
//        if (resourceType == RESOURCE_ASSETS) {
//            return resourcePath + file;
//        }
        return resDir + "/" + file;
    }
    
    public void getLocationUrl(final GetDomainCallBack callBack) {
		NetUtil.http().get(GET_LAUNCHER_LOCATION_URL).useCache(CacheBuilder.CACHE_NO).build().startAsync(new CallBack<LocationResp>() {

			@Override
			public void onSuccess(LocationResp t) {
				if (t.ResultCode == 0 && !TextUtils.isEmpty(t.LauncherXMLLocation)) {
					locationXmlUrl = t.LauncherXMLLocation;
					if (callBack != null) {
						callBack.onOk();
					}
				} else {
					if (callBack != null) {
						callBack.onFail();
					}
				}
			}

			@Override
			public void onFailure(Failure failure) {
				Log.e("ZTELauncher", failure.getFailDetail() + "");
				if (callBack != null) {
					callBack.onFail();
				}
			}
		});
	}

    private boolean unZipFile(String input, String output) {
        long extractedSize = 0L;
        Enumeration<ZipEntry> entries;
        ZipFile zip = null;
        try {
            zip = new ZipFile(input);
//            long uncompressedSize = getOriginalSize(zip);
//            publishProgress(0, (int) uncompressedSize);

            entries = (Enumeration<ZipEntry>) zip.entries();
            while (entries.hasMoreElements()) {
                ZipEntry entry = entries.nextElement();
                if (entry.isDirectory()) {
                    continue;
                }
                File destination = new File(output, entry.getName());
                if (!destination.getParentFile().exists()) {
//                    Log.e(TAG, "make=" + destination.getParentFile().getAbsolutePath());
                    destination.getParentFile().mkdirs();
                }
                if (destination.exists() && appContext != null) {

                }
                FileOutputStream outStream = new FileOutputStream(destination);
                extractedSize += copy(zip.getInputStream(entry), outStream);
                outStream.close();
            }
        } catch (ZipException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            Log.e("ZTELauncher", e.getMessage());
            return false;
        } catch (IOException e) {
        	Log.e("ZTELauncher", e.getMessage());
            e.printStackTrace();
            return false;
        } finally {
            try {
                zip.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                Log.e("ZTELauncher", e.getMessage());
            }
        }

        return true;
    }

    private int copy(InputStream input, OutputStream output) {
        byte[] buffer = new byte[1024 * 8];
        BufferedInputStream in = new BufferedInputStream(input, 1024 * 8);
        BufferedOutputStream out = new BufferedOutputStream(output, 1024 * 8);
        int count = 0, n = 0;
        try {
            while ((n = in.read(buffer, 0, 1024 * 8)) != -1) {
                out.write(buffer, 0, n);
                count += n;
            }
            out.flush();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            Log.e("ZTELauncher", e.getMessage());
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                Log.e("ZTELauncher", e.getMessage());
            }
            try {
                in.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                Log.e("ZTELauncher", e.getMessage());
            }
        }
        return count;
    }

    private void copy(String strOutFileName,String fileAssetPath) throws IOException {
        OutputStream myOutput = new FileOutputStream(strOutFileName);
        InputStream myInput = appContext.getAssets().open(fileAssetPath);
        byte[] buffer = new byte[1024];
        int length = myInput.read(buffer);
        while(length > 0)
        {
            myOutput.write(buffer, 0, length);
            length = myInput.read(buffer);
        }

        myOutput.flush();
        myInput.close();
        myOutput.close();
    }

//    private long getOriginalSize(ZipFile file) {
//        Enumeration<ZipEntry> entries = (Enumeration<ZipEntry>) file.entries();
//        long originalSize = 0l;
//        while (entries.hasMoreElements()) {
//            ZipEntry entry = entries.nextElement();
//            if (entry.getSize() >= 0) {
//                originalSize += entry.getSize();
//            }
//        }
//        return originalSize;
//    }

//    private final class ProgressReportingOutputStream extends FileOutputStream {
//
//        public ProgressReportingOutputStream(File file)
//                throws FileNotFoundException {
//            super(file);
//            // TODO Auto-generated constructor stub
//        }
//
//        @Override
//        public void write(byte[] buffer, int byteOffset, int byteCount)
//                throws IOException {
//            // TODO Auto-generated method stub
//            super.write(buffer, byteOffset, byteCount);
////            mProgress += byteCount;
////            publishProgress(mProgress);
//        }
//
//    }
}
